﻿/*

    Copyright (C) 2008, Shiropika, shiropika@gmail.com

    *KEEP THIS HEADER INFORMATION*
*/


using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Reflection;


namespace Rmlife.Common
{
    public class SmartExcel
    {
        [DllImport("kernel32.dll")]
        private static extern void RtlMoveMemory(ref string lpvDest, ref short lpvSource, int cbCopy);
        private FileStream fs;
        private BEG_FILE_RECORD m_udtBEG_FILE_MARKER;
        private END_FILE_RECORD m_udtEND_FILE_MARKER;
        private HPAGE_BREAK_RECORD m_udtHORIZ_PAGE_BREAK;


        //create an array that will hold the rows where a horizontal page break will be inserted just before.
        private int[] m_shtHorizPageBreakRows;
        private int m_shtNumHorizPageBreaks = 1;

        private void FilePut(byte[] buf)
        {
            fs.Write(buf, 0, buf.Length);
        }

        private void FilePut(System.ValueType vt)
        {
            Type t = vt.GetType();
            int size = 0;

            //			foreach(FieldInfo fi in t.GetFields())
            //			{
            //				size += Marshal.SizeOf(fi.FieldType);
            //				
            //				System.Diagnostics.Trace.WriteLine(fi.Name);
            //			}

            size = Marshal.SizeOf(vt);
            IntPtr p = Marshal.AllocHGlobal(size);
            Marshal.StructureToPtr(vt, p, true);
            byte[] buf = new byte[size];
            Marshal.Copy(p, buf, 0, size);

            fs.Write(buf, 0, buf.Length);
            Marshal.FreeHGlobal(p);
        }

        private void FilePut(System.ValueType vt, int len)
        {
            int size = 0;
            size = len;
            IntPtr p = Marshal.AllocHGlobal(size);
            Marshal.StructureToPtr(vt, p, true);
            byte[] buf = new byte[size];
            Marshal.Copy(p, buf, 0, size);

            fs.Write(buf, 0, buf.Length);
            Marshal.FreeHGlobal(p);
        }

        public bool PrintGridLines
        {
            set
            {
                try
                {
                    PRINT_GRIDLINES_RECORD GRIDLINES_RECORD;

                    GRIDLINES_RECORD.opcode = 43;
                    GRIDLINES_RECORD.length = 2;
                    if (true == value)
                    {
                        GRIDLINES_RECORD.PrintFlag = 1;
                    }
                    else
                    {
                        GRIDLINES_RECORD.PrintFlag = 0;
                    }
                    FilePut(GRIDLINES_RECORD);
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
        }

        public bool ProtectSpreadsheet
        {
            set
            {
                try
                {
                    PROTECT_SPREADSHEET_RECORD PROTECT_RECORD;


                    PROTECT_RECORD.opcode = 18;
                    PROTECT_RECORD.length = 2;
                    if (true == value)
                    {
                        PROTECT_RECORD.Protect = 1;
                    }
                    else
                    {
                        PROTECT_RECORD.Protect = 0;
                    }

                    if (null == fs) throw new SmartExcelOpeartionFileException();
                    FilePut(PROTECT_RECORD);
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
        }
        //
        public void CreateFile(string strFileName)
        {
            try
            {
                if (File.Exists(strFileName))
                {
                    File.SetAttributes(strFileName, FileAttributes.Normal);
                    File.Delete(strFileName);
                }

                fs = new FileStream(strFileName, FileMode.CreateNew);
                FilePut(m_udtBEG_FILE_MARKER);

                WriteDefaultFormats();
                //'create the Horizontal Page Break array
                m_shtHorizPageBreakRows = new int[1] { 0 };
                m_shtNumHorizPageBreaks = 0;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public void CloseFile()
        {
            short x;
            try
            {
                if (null != fs)
                {
                    //'write the horizontal page breaks if necessary
                    int lLoop1, lLoop2, lTemp;

                    if (m_shtNumHorizPageBreaks > 0)
                    {

                        for (lLoop1 = m_shtHorizPageBreakRows.GetUpperBound(0); lLoop1 >= m_shtHorizPageBreakRows.GetLowerBound(0); lLoop1--)
                        {
                            for (lLoop2 = m_shtHorizPageBreakRows.GetLowerBound(0) + 1; lLoop2 <= lLoop1; lLoop2++)
                            {
                                if (m_shtHorizPageBreakRows[lLoop2 - 1] > m_shtHorizPageBreakRows[lLoop2])
                                {
                                    lTemp = m_shtHorizPageBreakRows[lLoop2 - 1];
                                    m_shtHorizPageBreakRows[lLoop2 - 1] = m_shtHorizPageBreakRows[lLoop2];
                                    m_shtHorizPageBreakRows[lLoop2] = (short)lTemp;
                                }
                            }
                        }
                        //'write the Horizontal Page Break Record
                        m_udtHORIZ_PAGE_BREAK.opcode = 27;
                        m_udtHORIZ_PAGE_BREAK.length = (short)(2 + (m_shtNumHorizPageBreaks * 2));
                        m_udtHORIZ_PAGE_BREAK.NumPageBreaks = (short)m_shtNumHorizPageBreaks;

                        FilePut(m_udtHORIZ_PAGE_BREAK);

                        //				 'now write the actual page break values
                        //				'the MKI$ function is standard in other versions of BASIC but
                        //				'VisualBasic does not have it. A KnowledgeBase article explains
                        //				'how to recreate it (albeit using 16-bit API, I switched it
                        //				'to 32-bit).
                        for (x = 1; x <= m_shtHorizPageBreakRows.GetUpperBound(0); x++)
                        {
                            FilePut(System.Text.Encoding.Default.GetBytes(MKI((short)(m_shtHorizPageBreakRows[x]))));
                        }
                    }
                    FilePut(m_udtEND_FILE_MARKER);
                    fs.Close();
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        private void Init()
        {
            //		'Set up default values for records
            //		'These should be the values that are the same for every record of these types

            m_udtBEG_FILE_MARKER.opcode = 9;
            m_udtBEG_FILE_MARKER.length = 4;
            m_udtBEG_FILE_MARKER.version = 2;
            m_udtBEG_FILE_MARKER.ftype = 10;

            // end of file marker
            m_udtEND_FILE_MARKER.opcode = 10;
        }

        public SmartExcel()
        {
            Init();
        }


        public void InsertHorizPageBreak(int lrow)
        {
            int row;
            try
            {
                //	'the row and column values are written to the excel file as
                //	'unsigned integers. Therefore, must convert the longs to integer.
                if (lrow > 32767 || lrow < 0) row = 0;
                else row = lrow - 1;
                m_shtNumHorizPageBreaks = m_shtNumHorizPageBreaks + 1;
                m_shtHorizPageBreakRows[m_shtNumHorizPageBreaks] = row;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public void WriteValue(ValueTypes ValueType, CellFont CellFontUsed, CellAlignment Alignment, CellHiddenLocked HiddenLocked, int lrow, int lcol, object Value)
        {
            WriteValue(ValueType, CellFontUsed, Alignment, HiddenLocked, lrow, lcol, Value, 0);
        }

        public void WriteValue(ValueTypes ValueType, CellFont CellFontUsed, CellAlignment Alignment, CellHiddenLocked HiddenLocked, int lrow, int lcol, object Value, int CellFormat)
        {
            int l;
            string st;
            short col, row;
            try
            {
                tInteger INTEGER_RECORD;
                tNumber NUMBER_RECORD;
                //byte b;
                tText TEXT_RECORD;
                if (lrow > 32767 || lrow < 0)
                {
                    row = 0;
                    throw new Exception("行号不能大于32767！");
                }
                else row = (short)(lrow - 1);
                if (lcol > 32767 || lcol < 0) col = 0; else col = (short)(lcol - 1);
                switch (ValueType)
                {
                    case ValueTypes.Integer:
                        INTEGER_RECORD.opcode = 2;
                        INTEGER_RECORD.length = 9;
                        INTEGER_RECORD.row = row;
                        INTEGER_RECORD.col = col;
                        INTEGER_RECORD.rgbAttr1 = (byte)(HiddenLocked);
                        INTEGER_RECORD.rgbAttr2 = (byte)(CellFontUsed + CellFormat);
                        INTEGER_RECORD.rgbAttr3 = (byte)(Alignment);
                        INTEGER_RECORD.intValue = (short)(Value);
                        FilePut(INTEGER_RECORD);
                        break;
                    case ValueTypes.Number:
                        NUMBER_RECORD.opcode = 3;
                        NUMBER_RECORD.length = 15;
                        NUMBER_RECORD.row = row;
                        NUMBER_RECORD.col = col;
                        NUMBER_RECORD.rgbAttr1 = (byte)(HiddenLocked);
                        NUMBER_RECORD.rgbAttr2 = (byte)(CellFontUsed + CellFormat);
                        NUMBER_RECORD.rgbAttr3 = (byte)(Alignment);
                        NUMBER_RECORD.NumberValue = (double)(Value);
                        FilePut(NUMBER_RECORD);
                        break;
                    case ValueTypes.Text:
                        st = Convert.ToString(Value);
                        l = GetLength(st);// 'LenB(StrConv(st, vbFromUnicode)) 'Len(st$)

                        TEXT_RECORD.opcode = 4;
                        TEXT_RECORD.length = 10;
                        //'Length of the text portion of the record
                        TEXT_RECORD.TextLength = (byte)l;
                        //						  'Total length of the record
                        TEXT_RECORD.length = (byte)(8 + l);
                        TEXT_RECORD.row = row;
                        TEXT_RECORD.col = col;
                        TEXT_RECORD.rgbAttr1 = (byte)(HiddenLocked);
                        TEXT_RECORD.rgbAttr2 = (byte)(CellFontUsed + CellFormat);
                        TEXT_RECORD.rgbAttr3 = (byte)(Alignment);
                        //						'Put record header
                        FilePut(TEXT_RECORD);
                        //				'Then the actual string data
                        FilePut(System.Text.Encoding.Default.GetBytes(st));
                        break;
                    default: break;
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public void SetMargin(MarginTypes Margin, double MarginValue)
        {
            try
            {
                //					  'write the spreadsheet's layout information (in inches)
                MARGIN_RECORD_LAYOUT MarginRecord;
                MarginRecord.opcode = (short)Margin;
                MarginRecord.length = 8;
                MarginRecord.MarginValue = MarginValue;// 'in inches
                FilePut(MarginRecord);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public void SetColumnWidth(int FirstColumn, int LastColumn, short WidthValue)
        {
            try
            {
                COLWIDTH_RECORD COLWIDTH;

                COLWIDTH.opcode = 36;
                COLWIDTH.length = 4;
                COLWIDTH.col1 = (byte)(FirstColumn - 1);
                COLWIDTH.col2 = (byte)(LastColumn - 1);
                COLWIDTH.ColumnWidth = (short)(WidthValue * 256);// 'values are specified as 1/256 of a character
                FilePut(COLWIDTH);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public void SetFont(string FontName, short FontHeight, FontFormatting FontFormat)
        {
            int l;
            try
            {
                //	 'you can set up to 4 fonts in the spreadsheet file. When writing a value such
                //	'as a Text or Number you can specify one of the 4 fonts (numbered 0 to 3)
                FONT_RECORD FONTNAME_RECORD;
                l = GetLength(FontName);// 'LenB(StrConv(FontName, vbFromUnicode)) 'Len(FontName)
                FONTNAME_RECORD.opcode = 49;
                FONTNAME_RECORD.length = (short)(5 + l);
                FONTNAME_RECORD.FontHeight = (short)(FontHeight * 20);
                FONTNAME_RECORD.FontAttributes1 = (byte)FontFormat;// 'bold/underline etc...
                FONTNAME_RECORD.FontAttributes2 = (byte)0;// 'reserved-always zero!!
                FONTNAME_RECORD.FontNameLength = (byte)l;//'CByte(Len(FontName))
                FilePut(FONTNAME_RECORD);
                //						'Then the actual font name data
                FilePut(System.Text.Encoding.Default.GetBytes(FontName));
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public void SetHeader(string HeaderText)
        {
            int l;
            try
            {
                HEADER_FOOTER_RECORD HEADER_RECORD;
                l = GetLength(HeaderText);//   'LenB(StrConv(HeaderText, vbFromUnicode)) 'Len(HeaderText)
                HEADER_RECORD.opcode = 20;
                HEADER_RECORD.length = (short)(1 + l);
                HEADER_RECORD.TextLength = (byte)l;// 'CByte(Len(HeaderText))
                FilePut(HEADER_RECORD);
                //						'Then the actual Header text
                FilePut(System.Text.Encoding.Default.GetBytes(HeaderText));
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public void SetFooter(string FooterText)
        {
            int l;
            try
            {
                HEADER_FOOTER_RECORD FOOTER_RECORD;
                l = GetLength(FooterText);// 'LenB(StrConv(FooterText, vbFromUnicode)) 'Len(FooterText)
                FOOTER_RECORD.opcode = 21;
                FOOTER_RECORD.length = (short)(1 + l);
                FOOTER_RECORD.TextLength = (byte)l;
                FilePut(FOOTER_RECORD);
                //					'Then the actual Header text
                FilePut(System.Text.Encoding.Default.GetBytes(FooterText));
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        public void SetFilePassword(string PasswordText)
        {
            int l;
            try
            {
                PASSWORD_RECORD FILE_PASSWORD_RECORD;
                l = GetLength(PasswordText);// 'LenB(StrConv(PasswordText, vbFromUnicode)) 'Len(PasswordText)
                FILE_PASSWORD_RECORD.opcode = 47;
                FILE_PASSWORD_RECORD.length = (short)l;
                FilePut(FILE_PASSWORD_RECORD);
                //		  'Then the actual Password text
                FilePut(System.Text.Encoding.Default.GetBytes(PasswordText));
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        private void WriteDefaultFormats()
        {
            FORMAT_COUNT_RECORD cFORMAT_COUNT_RECORD;
            FORMAT_RECORD cFORMAT_RECORD;
            int lIndex;
            int l;
            string q = "\"";
            string[] aFormat = new string[]{
											   "General",
											   "0",
											   "0.00",
											   "#,##0",
											   "#,##0.00",
											   "#,##0\\ "+q+"$"+q+";\\-#,##0\\ "+q+"$"+q,
											   "#,##0\\ "+q+"$"+q+";[Red]\\-#,##0\\ "+q+"$"+q,
											   "#,##0.00\\ "+q+"$"+q+";\\-#,##0.00\\ "+q+"$"+q,
											   "#,##0.00\\ "+q+"$"+q+";[Red]\\-#,##0.00\\ "+q+"$"+q,
											   "0%",
											   "0.00%",
											   "0.00E+00",
											   "dd/mm/yy",
											   "dd/\\ mmm\\ yy",
											   "dd/\\ mmm",
											   "mmm\\ yy",
											   "h:mm\\ AM/PM",
											   "h:mm:ss\\ AM/PM",
											   "hh:mm",
											   "hh:mm:ss",
											   "dd/mm/yy\\ hh:mm",
											   "##0.0E+0",
											   "mm:ss",
											   "@"};


            cFORMAT_COUNT_RECORD.opcode = 0x1f;
            cFORMAT_COUNT_RECORD.length = 0x02;
            cFORMAT_COUNT_RECORD.Count = (short)(aFormat.GetUpperBound(0));
            FilePut(cFORMAT_COUNT_RECORD);

            byte b;
            int a;
            for (lIndex = aFormat.GetLowerBound(0); lIndex <= aFormat.GetUpperBound(0); lIndex++)
            {
                l = aFormat[lIndex].Length;
                cFORMAT_RECORD.opcode = 0x1e;
                cFORMAT_RECORD.length = (short)(l + 1);
                cFORMAT_RECORD.FormatLength = (byte)(l);
                FilePut(cFORMAT_RECORD);
                //				Then the actual format
                // 从1开始还是从0开始？！
                for (a = 0; a < l; a++)
                {
                    b = (byte)(aFormat[lIndex].Substring(a, 1).ToCharArray(0, 1)[0]);
                    FilePut(new byte[] { b });
                }
            }
        }

        private string MKI(short x)
        {
            string temp;
            //'used for writing integer array values to the disk file
            temp = "  ";
            RtlMoveMemory(ref temp, ref x, 2);
            return temp;
        }

        private int GetLength(string strText)
        {
            return System.Text.Encoding.Default.GetBytes(strText).Length;
        }

        public void SetDefaultRowHeight(int HeightValue)
        {
            try
            {
                //			  'Height is defined in units of 1/20th of a point. Therefore, a 10-point font
                //				'would be 200 (i.e. 200/20 = 10). This function takes a HeightValue such as
                //				'14 point and converts it the correct size before writing it to the file.

                DEF_ROWHEIGHT_RECORD DEFHEIGHT;
                DEFHEIGHT.opcode = 37;
                DEFHEIGHT.length = 2;
                DEFHEIGHT.RowHeight = HeightValue * 20;//  'convert points to 1/20ths of point
                FilePut(DEFHEIGHT);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public void SetRowHeight(int Row, short HeightValue)
        {
            int o_intRow;
            //			   'the row and column values are written to the excel file as
            //				'unsigned integers. Therefore, must convert the longs to integer.
            if (Row > 32767) throw new Exception("行号不能大于32767！");
            try
            {
                o_intRow = Row;
                //				if(Row > 32767){
                //								   o_intRow = CInt(Row - 65536)
                //								   Else
                //									   o_intRow = CInt(Row) - 1    'rows/cols in Excel binary file are zero based
                //																   }

                //					'Height is defined in units of 1/20th of a point. Therefore, a 10-point font
                //				'would be 200 (i.e. 200/20 = 10). This function takes a HeightValue such as
                //				'14 point and converts it the correct size before writing it to the file.

                ROW_HEIGHT_RECORD ROWHEIGHTREC;
                ROWHEIGHTREC.opcode = 8;
                ROWHEIGHTREC.length = 16;
                ROWHEIGHTREC.RowNumber = o_intRow;
                ROWHEIGHTREC.FirstColumn = 0;
                ROWHEIGHTREC.LastColumn = 256;
                ROWHEIGHTREC.RowHeight = HeightValue * 20;// 'convert points to 1/20ths of point
                ROWHEIGHTREC.internals = 0;
                ROWHEIGHTREC.DefaultAttributes = 0;
                ROWHEIGHTREC.FileOffset = 0;
                ROWHEIGHTREC.rgbAttr1 = 0;
                ROWHEIGHTREC.rgbAttr2 = 0;
                ROWHEIGHTREC.rgbAttr3 = 0;
                FilePut(ROWHEIGHTREC);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
    }


    public enum ValueTypes
    {
        Integer = 0,
        Number = 1,
        Text = 2,
    }

    public enum CellAlignment
    {
        GeneralAlign = 0,
        LeftAlign = 1,
        CentreAlign = 2,
        RightAlign = 3,
        FillCell = 4,
        LeftBorder = 8,
        RightBorder = 16,
        TopBorder = 32,
        BottomBorder = 64,
        Shaded = 128
    }

    //	'used by rgbAttr2
    //'bits 0-5 handle the *picture* formatting, not bold/underline etc...
    //'bits 6-7 handle the font number
    public enum CellFont
    {
        Font0 = 0,
        Font1 = 64,
        Font2 = 128,
        Font3 = 192,
    }

    //	'used by rgbAttr1
    //	'bits 0-5 must be zero
    //	'bit 6 locked/unlocked
    //	'bit 7 hidden/not hidden
    public enum CellHiddenLocked
    {
        Normal = 0,
        Locked = 64,
        Hidden = 128,
    }

    public enum MarginTypes
    {
        LeftMargin = 38,
        RightMargin = 39,
        TopMargin = 40,
        BottomMargin = 41,
    }

    /// <summary>
    /// 
    /// </summary>
    /// <remarks>可以使用|符号进行或组合</remarks>
    public enum FontFormatting
    {
        NoFormat = 0,
        Bold = 1,
        Italic = 2,
        Underline = 4,
        Strikeout = 8
    }

    public class SmartExcelOpeartionFileException : ApplicationException
    {
        public SmartExcelOpeartionFileException()
            : base("请首先调用CreateFile方法!")
        {

        }
    }



    [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 1)]
    struct FONT_RECORD
    {
        public short opcode;//49
        public short length;//5+len(fontname)
        public short FontHeight;
        [MarshalAs(UnmanagedType.I1, SizeConst = 1)]
        public byte FontAttributes1;//bit0 bold, bit1 italic, bit2 underline, bit3 strikeout, bit4-7 reserved
        [MarshalAs(UnmanagedType.I1, SizeConst = 1)]
        public byte FontAttributes2;//reserved - always 0
        [MarshalAs(UnmanagedType.I1, SizeConst = 1)]
        public byte FontNameLength;
    }

    [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 1)]
    struct PASSWORD_RECORD
    {
        public short opcode;//47
        public short length;//len(password)
    }

    [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 1)]
    struct HEADER_FOOTER_RECORD
    {
        public short opcode;//20 Header, 21 Footer
        public short length;//1+len(text)
        [MarshalAs(UnmanagedType.I1, SizeConst = 1)]
        public byte TextLength;
    }

    [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 1)]
    struct PROTECT_SPREADSHEET_RECORD
    {
        public short opcode;//18
        public short length;//2
        public short Protect;
    }

    [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 1)]
    struct FORMAT_COUNT_RECORD
    {
        public short opcode;//0x1f
        public short length;//2
        public short Count;
    }

    [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 1)]
    struct FORMAT_RECORD
    {
        public short opcode;// 0x1e
        public short length;//1+len(format)
        [MarshalAs(UnmanagedType.U1, SizeConst = 1)]
        public byte FormatLength;//len(format)
    }//followed by the Format-Picture

    [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 1)]
    struct COLWIDTH_RECORD
    {
        public short opcode;//36
        public short length;//4
        [MarshalAs(UnmanagedType.I1, SizeConst = 1)]
        public byte col1;//first column
        [MarshalAs(UnmanagedType.I1, SizeConst = 1)]
        public byte col2;//last column
        public short ColumnWidth;//at 1/256th of a character
    }

    //	'Beginning Of File record
    [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 1)]
    struct BEG_FILE_RECORD
    {
        public short opcode;
        public short length;
        public short version;
        public short ftype;
    }

    //	'End Of File record
    [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 1)]
    struct END_FILE_RECORD
    {
        public short opcode;
        public short length;
    }


    //	'true/false to print gridlines
    [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 1)]
    struct PRINT_GRIDLINES_RECORD
    {
        public short opcode;
        public short length;
        public short PrintFlag;
    }

    //	'Integer record
    [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 1)]
    struct tInteger
    {
        public short opcode;
        public short length;
        public short row;//unsigned integer
        public short col;
        [MarshalAs(UnmanagedType.I1, SizeConst = 1)]
        public byte rgbAttr1;//rgbAttr1 handles whether cell is hidden and/or locked		
        [MarshalAs(UnmanagedType.I1, SizeConst = 1)]
        public byte rgbAttr2;//rgbAttr2 handles the Font# and Formatting assigned to this cell		
        [MarshalAs(UnmanagedType.I1, SizeConst = 1)]
        public byte rgbAttr3;//rgbAttr3 handles the Cell Alignment/borders/shading
        public short intValue;//the actual integer value
    }

    //	'Number record
    [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 1)]
    struct tNumber
    {
        public short opcode;
        public short length;
        public short row;
        public short col;
        [MarshalAs(UnmanagedType.I1, SizeConst = 1)]
        public byte rgbAttr1;
        [MarshalAs(UnmanagedType.I1, SizeConst = 1)]
        public byte rgbAttr2;
        [MarshalAs(UnmanagedType.I1, SizeConst = 1)]
        public byte rgbAttr3;
        public double NumberValue;//8 Bytes
    }
    //
    //	'Label (Text) record
    [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 1)]
    struct tText
    {
        public short opcode;
        public short length;
        public short row;
        public short col;
        [MarshalAs(UnmanagedType.I1, SizeConst = 1)]
        public byte rgbAttr1;
        [MarshalAs(UnmanagedType.I1, SizeConst = 1)]
        public byte rgbAttr2;
        [MarshalAs(UnmanagedType.I1, SizeConst = 1)]
        public byte rgbAttr3;
        [MarshalAs(UnmanagedType.I1, SizeConst = 1)]
        public byte TextLength;
    }
    //
    [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 1)]
    struct MARGIN_RECORD_LAYOUT
    {
        public short opcode;
        public short length;
        public double MarginValue;//8 bytes
    }
    //
    [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 1)]
    struct HPAGE_BREAK_RECORD
    {
        public short opcode;
        public short length;
        public short NumPageBreaks;
    }
    //
    [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 1)]
    struct DEF_ROWHEIGHT_RECORD
    {
        public int opcode;
        public int length;
        public int RowHeight;
    }
    //
    [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 1)]
    struct ROW_HEIGHT_RECORD
    {
        public int opcode;//08
        public int length;//should always be 16 bytes
        public int RowNumber;
        public int FirstColumn;
        public int LastColumn;
        public int RowHeight;//written to file as 1/20ths of a point
        public int internals;
        [MarshalAs(UnmanagedType.I1, SizeConst = 1)]
        public byte DefaultAttributes;//set to zero for no default attributes
        public int FileOffset;
        [MarshalAs(UnmanagedType.I1, SizeConst = 1)]
        public byte rgbAttr1;
        [MarshalAs(UnmanagedType.I1, SizeConst = 1)]
        public byte rgbAttr2;
        [MarshalAs(UnmanagedType.I1, SizeConst = 1)]
        public byte rgbAttr3;
    }

}